package com.crm.action;

import java.util.*;

/**
 * @author <a href="mailto:huang0510@hotmail.com">HuangHuang</a>
 * @version 1.0
 */
public class JSONObject {

    private static final class Null {

        protected final Object clone() {
            return this;
        }

        public boolean equals(Object object) {
            return object == null || object == this;
        }

        public String toString() {
            return "null";
        }

    }

    private Map myLinkedHashMap;

    public static final Object NULL = new Null();

    public JSONObject() {
        this.myLinkedHashMap = new LinkedHashMap();
    }

    public JSONObject(Map map) {
        this.myLinkedHashMap = (map == null) ?
            new HashMap() :
            new HashMap(map);
    }

    public JSONObject(JSONObject json, String[] names) throws Exception {
        this();
        for (String name : names) {
            putOpt(name, json.opt(name));
        }
    }

    public JSONObject putOpt(String key, Object value) throws Exception {
        if (key != null && value != null) {
            put(key, value);
        }
        return this;
    }

    public JSONObject put(String key, Collection value) throws Exception {
        put(key, new JSONArray(value));
        return this;
    }

    public JSONObject put(String key, Object value) throws Exception {
        if (key == null) {
            throw new Exception("Null Key.");
        }
        if (value != null) {
            testValidity(value);
            this.myLinkedHashMap.put(key, value);
        } else {
            remove(key);
        }
        return this;
    }

    public JSONObject put(String key, boolean value) throws Exception {
        put(key, value ? Boolean.TRUE : Boolean.FALSE);
        return this;
    }

    static void testValidity(Object o) throws Exception {
        if (o != null) {
            if (o instanceof Double) {
                if (((Double) o).isInfinite() || ((Double) o).isNaN()) {
                    throw new Exception("JSON does not allow non-finite numbers.");
                }
            } else if (o instanceof Float) {
                if (((Float) o).isInfinite() || ((Float) o).isNaN()) {
                    throw new Exception("JSON does not allow non-finite numbers.");
                }
            }
        }
    }

    public Object remove(String key) {
        return this.myLinkedHashMap.remove(key);
    }

    public Object opt(String key) {
        return key == null ? null : this.myLinkedHashMap.get(key);
    }

    public String toString() {
        try {
            Iterator keys = keys();
            StringBuffer sb = new StringBuffer("{");

            while (keys.hasNext()) {
                if (sb.length() > 1) {
                    sb.append(',');
                }
                Object o = keys.next();
                sb.append(quote(o.toString()));
                sb.append(":");
                sb.append(valueToString(this.myLinkedHashMap.get(o)));
            }
            sb.append("}");
            return sb.toString();
        } catch (Exception e) {
            e.printStackTrace();
            return null;
        }
    }

    public Iterator keys() {
        return this.myLinkedHashMap.keySet().iterator();
    }

    public static String quote(String string) {
        if (string == null || string.length() == 0) {
            return "\"\"";
        }

        char b;
        char c = 0;
        int i;
        int len = string.length();
        StringBuffer sb = new StringBuffer(len + 4);
        String t;

        sb.append('"');
        for (i = 0; i < len; i += 1) {
            b = c;
            c = string.charAt(i);
            switch (c) {
                case '\\':
                case '"':
                    sb.append('\\');
                    sb.append(c);
                    break;
                case '/':
                    if (b == '<') {
                        sb.append('\\');
                    }
                    sb.append(c);
                    break;
                case '\b':
                    sb.append("\\b");
                    break;
                case '\t':
                    sb.append("\\t");
                    break;
                case '\n':
                    sb.append("\\n");
                    break;
                case '\f':
                    sb.append("\\f");
                    break;
                case '\r':
                    sb.append("\\r");
                    break;
                default:
                    if (c < ' ' || (c >= '\u0080' && c < '\u00a0') || (c >= '\u2000' && c < '\u2100')) {
                        t = "000" + Integer.toHexString(c);
                        sb.append("\\u").append(t.substring(t.length() - 4));
                    } else {
                        sb.append(c);
                    }
            }
        }
        sb.append('"');
        return sb.toString();
    }

    static String valueToString(Object value) throws Exception {
        if (value == null || value.equals(null)) {
            return "null";
        }
        if (value instanceof JSONString) {
            Object o = ((JSONString)value).toJSONString();
            if (o instanceof String) {
                return (String) o;
            }
            throw new Exception("Bad value from toJSONString: " + o);
        }
        if (value instanceof Number) {
            return numberToString((Number) value);
        }
        if (value instanceof Boolean || value instanceof JSONObject || value instanceof JSONArray) {
            return value.toString();
        }
        if (value instanceof Map) {
            return new JSONArray((Collection)value).toString();
        }                                                      
        if (value.getClass().isArray()) {
            return new JSONArray(value).toString();
        }
        return quote(value.toString());
    }

    static public String numberToString(Number n) throws Exception{
        if (n == null) {
            throw new NullPointerException("Null pinter");
        }
        testValidity(n);

        String s = n.toString();
        if (s.indexOf('.') > 0 && s.indexOf('e') < 0 && s.indexOf('E') < 0) {
            while (s.endsWith("0")) {
                s = s.substring(0, s.length() - 1);
            }
            if (s.endsWith(".")) {
                s = s.substring(0, s.length() - 1);
            }
        }
        return s;
    }
}
